home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
music
/
cthugha5.zip
/
CTHU5SRC.ZIP
/
SB_DRIVE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-19
|
36KB
|
1,678 lines
/* Soundblaster Digital Audio driver for Borland C++. *
* *
* Much of the program code is derived from various modules *
* of the VangeliSTracker program, specifically SOUNDBLA.PAS *
* and HARDWARE.PAS. Many thanks for making these wonderful *
* sources available to the public. They basically allowed *
* me to complete this project. *
* *
* Other portions of the code are derived from Michael *
* Fulbright and Steve Haehnichen's SBMSDOS v1.0 code. *
* *
* 31May94 zaph : Slight changes to compile under MSC *
* *
* Copyright (C) 1993 by Daniel Sachs *
* Parts Copyright (C) 1993 by VangeliSTeam *
* Parts Copyright (C) 1992 by Steve Haehnichen */
#define SB_DRIVE
// #define DEBUG_PROC
// #define DEBUG_DMASTAT
#include "sb_drive.h"
#include "dma.h"
#include <dos.h>
#include <stdlib.h>
#ifdef _MSC_VER
#include <memory.h>
#include "patch.h"
#define setvect(i,p) _dos_setvect(i,p)
#define getvect(i) _dos_getvect(i)
#define disable() _disable()
#define enable() _enable()
#else
#include <mem.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#define AUTO_PREWRITE // Uncomment to load entire buffer before
// starting playback. By default playback
// starts immediately.
int GUS = 0; // Welp, the GUS needs some special treatment. :)
//#define WARN_OS2 // Uncomment to warn about 16-bit DMA incompatibilty
//#define DEBUG // Uncomment for full debugging outputs.
//#define DEBUG_DMASTAT // Uncomment for DMA buffer status information
//#define DEBUG_PROC // Uncomment for debug information from most functions.
//#define DEBUG_WRITE // Uncomment for debug information from dsp_write
//#define DEBUG_FREE // Uncomment for debug information from dsp_bufs_free
#ifdef DEBUG
#define DEBUG_DMASTAT
#define DEBUG_PROC
#define DEBUG_WRITE
#define DEBUG_FREE
#endif
struct dsp_device_caps SB_caps[] =
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // No SB
{ 4000, 22222, 0, 0, 0, 11111, 0, 0, 0, 0 }, // SB 1.x
{ 4000, 45454, 0, 0, 0, 15151, 0, 0, 0, 0 }, // SB 2.x
{ 4000, 45454, 22727, 0, 0, 45454, 22727, 0, 0, 0 }, // SBPro
{ 4000, 45454, 45454, 45454, 45454, 45454, 45454, 45454, 45454, 1 } // SB16
};
int SBport = 0x220;
int SBirq = 5;
int SBdma = 1;
int SBdma16 = 5;
int SBintnum;
int SBtype;
int SBTimeOut = 5000; /* time to wait for DSP response */
int SBspeaker; // Speaker enabled?
unsigned char SBProMixRegs[] = { 0x22, 0x04, 0x26, 0x2E, 0x28, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00};
unsigned char SB16MixRegs[] = { 0x30, 0x32, 0x34, 0x38, 0x36, 0x41, 0x44, 0x46, 0x3F, 0x3A, 0x3B};
char SbOK;
int bufs_open;
char *DMABufferBase;
char *DMABuffers[16];
unsigned DMABufferSize;
unsigned BufLen;
unsigned BufCount;
unsigned BufTotal;
static char volatile BufQueueHead;
static char volatile BufQueueTail;
static int volatile DMABufsFull;
static int volatile DMAStopped = 1;
unsigned dsp_speed;
char dsp_bits;
char dsp_stereo;
char dsp_hispeed;
char dsp_signed;
int volatile dsp_overrun;
#ifdef _MSC_VER
void (_interrupt _far *old_irq_handler)();
#else
void interrupt (*old_irq_handler)(void);
#endif
void interrupt write_irq_handler(void);
void interrupt null_irq_handler(void);
static void (*callback_function)(void);
char BitMasks[] = {0xFE, /* 1111 1110 */
0xFD, /* 1111 1101 */
0xFB, /* 1111 1011 */
0xF7, /* 1111 0111 */
0xEF, /* 1110 1111 */
0xDF, /* 1101 1111 */
0xBF, /* 1011 1111 */
0x7F};/* 0111 1111 */
void SbWriteLoop(unsigned t)
{
_asm {
MOV BX,t
MOV DX,[SBport]
ADD DX,DSPWriteOffset
}
lp: _asm {
DEC BX
JZ end
IN AL,DX
ADD AL,AL
JC lp
}
end: _asm {
OR BL,BH
MOV [SbOK],BL
}
}
void SbWriteByteTimeout(unsigned char command, unsigned timeout)
{
SbWriteLoop(timeout);
inp(DSPReadPort);
SbWriteLoop(timeout);
outp(DSPWritePort,command);
}
void SbWriteByte(unsigned char command)
{
SbWriteLoop(SBTimeOut);
inp(DSPReadPort);
SbWriteLoop(SBTimeOut);
outp(DSPWritePort,command);
}
void SbReadLoop(unsigned t)
{
_asm MOV BX,t
_asm MOV DX,[SBport]
_asm ADD DX,[DSPRStatOffset]
lp: _asm DEC BX
_asm JZ fin
_asm IN AL,DX
_asm ADD AL,AL
_asm JNC lp
fin: _asm OR BL,BH
_asm MOV [SbOK],BL
}
unsigned char SbReadByte(unsigned timeout)
{
SbReadLoop(timeout);
return inp(DSPReadPort);
}
void SbWriteMixerReg(unsigned char reg, unsigned char value)
{
outp(MixAddrPort,reg);
outp(MixDataPort,value);
}
char SbReadMixerReg(unsigned char reg)
{
outp(MixAddrPort,reg);
return inp(MixDataPort);
}
void EnableIRQ(char irq)
{
disable();
if( irq < 8 )
outp(0x21,inp(0x21) & BitMasks[irq]);
else
{
outp(0x21,inp(0x21) & BitMasks[1]);
outp(0xA1,inp(0xA1) & BitMasks[irq-8]);
}
enable();
}
void DisableIRQ(char irq)
{
disable();
if( irq < 8 )
outp(0x21,inp(0x21) | (BitMasks[irq])^(0xFF));
else
outp(0xA1,inp(0xA1) | (BitMasks[irq-8])^(0xFF));
enable();
}
int dsp_reset(void)
{
int stat;
int ct;
SBspeaker = 0;
outp(DSPResetPort,1);
delay(50);
outp(DSPResetPort,0);
for( ct=0; ct<100; ct++ ) // Reset DSP delay loop
{
inp(DSPRStatPort);
stat = inp(DSPReadPort);
if( stat == 0xAA )
break;
}
if( stat != 0xAA )
return 0;
SBtype = (dsp_version() / 100); // Return card type
#ifdef DEBUG_PROC
printf("dsp_reset: type %i\n",SBtype);
#endif
return SBtype;
}
int dsp_version(void)
{
static int minor=0;
static int major=0;
int i;
if( major == 0 )
{
SbWriteByte(DSPGetVersion); /* Command to get DSP version */
for( i=0; i<10; i++ ) // Wait for card to respond
{
major=SbReadByte(0xFFFF);
if( (major != 0xAA) /* && (SbOK) */ )
break;
}
if( i==10 )
return 0;
minor = SbReadByte(SBTimeOut);
}
#ifdef DEBUG_PROC
printf("dsp_version: %i.%02i\n",major,minor);
#endif
return ((major*100) + minor); // Return numeric version number
}
void dsp_speaker(int status)
{
#ifdef DEBUG_PROC
printf("dsp_speaker: %s\n",status ? "on" : "off" );
#endif
if( SBtype >= 4 )
return;
status = !!status;
if( status != SBspeaker )
if( status )
SbWriteByte(DSPSpeakerOn);
else
SbWriteByte(DSPSpeakerOff);
SBspeaker = status;
delay(50);
}
int dsp_open(int port, int dma, int irq, int dma16, unsigned bufsize, unsigned numbufs)
{
char *work;
long wlong;
int page;
unsigned offset;
unsigned segment;
unsigned tot_bufsize;
int i;
work = getenv("ULTRASND"); // Check to see if we have a GUS
if( work != NULL )
{
GUS = 1;
#ifdef DEBUG_PROC
printf("dsp_open: Gravis Ultrasound detected.\n");
#endif
}
else
GUS = 0;
#ifdef DEBUG_PROC
printf("dsp_open: port %x dma %i irq %i dma16 %i bufsize %i numbufs %i\n",port,dma,irq,dma16,bufsize,numbufs);
#endif
SBport = port; // Set the configuration flags
SBdma = dma;
SBirq = irq;
SBdma16 = dma16;
if(SBirq == 2)
SBirq = 9;
SBintnum = SBirq + 8 + 96*(SBirq>7);
if( (DMABufferBase != NULL) && ((numbufs != BufCount) || (bufsize != BufLen)) )
return 0; // Sorry, once you set up buffers you're stuck with 'em.
// All you can do is change the PORT/IRQ/DMA settings.
if( (numbufs < 2) || (numbufs > 16) || (bufsize < 128) || (bufsize > 31774) )
return 0; // Too many or too long buffers?
if( bufsize % 4 ) // Buffer size has to be devisible by 4
bufsize = 4*((bufsize+4)/4);
if( ((long)bufsize * numbufs) > 64000 ) // and can't be longer than 64K
return 0;
tot_bufsize = bufsize * numbufs; // How much we need to allocate
if( !dsp_reset() ) // Reset the DSP. Is it there?
return 0;
#ifdef WARN_OS2
if( (SBtype == 4) && (dma16 >= 4) && (_osmajor >= 20) )
{
printf("Warning: 16-bit DMA does not work properly with OS/2.\n\n"
"Press <Esc> to abort to DOS and select 8-bit DMA.\n"
"Press <Enter> to ignore this warning and continue.\n");
i = 0;
while( (i != 27) && (i != 13) )
i = getch();
if( i == 27 )
exit(100);
}
#endif
atexit(dsp_close); // Finish up when program exits
BufLen = bufsize; // Set up buffer configuration
BufCount = numbufs;
BufTotal = tot_bufsize;
if( DMABufferBase == NULL ) // Make sure we haven't
{ // done this already :)
work = calloc(1,tot_bufsize+1);
if( work == NULL )
return 0;
// Allocate page-safe buffer:
offset = ((FP_SEG(work)&0x0FFF)<<4) + FP_OFF(work);
if( ((long)offset+tot_bufsize) > 65535L ) // Current buffer page-safe?
{
realloc(work,65536L-offset); // No: reallocate it
DMABufferBase = calloc(1,tot_bufsize+1);// This should be :)
free(work);
offset = ((FP_SEG(DMABufferBase)&0x0FFF)<<4) + FP_OFF(DMABufferBase);
if( ((long)offset+tot_bufsize) > 65535L )
{
printf("Unable to allocate page-safe buffer\n");
exit(100);
}
}
else
DMABufferBase = work; // Current buffer OK
if( DMABufferBase == NULL )
return 0;
DMABufferSize = tot_bufsize; // Set up buffer pointers
for( i = 0; i < numbufs; i++ )
DMABuffers[i] = DMABufferBase + i*bufsize;
}
#ifdef _MSC_VER
old_irq_handler = _dos_getvect(SBintnum); // Save old interrupt
_dos_setvect(SBintnum,null_irq_handler); // ...and stick ours in
#else
old_irq_handler = getvect(SBintnum); // Save old interrupt
setvect(SBintnum,null_irq_handler); // ...and stick ours in
#endif
callback_function = NULL;
disable();
BufQueueHead = 0; // We're not playing anything
BufQueueTail = 0;
DMABufsFull = 0;
DMAStopped = 1;
enable();
EnableIRQ(SBirq);
dsp_speaker(1);
return SBtype;
}
void dsp_close(void)
{
#ifdef DEBUG_PROC
printf("dsp_close\n");
#endif
dsp_pause_dma();
dma_reset(SBdma);
dma_reset(SBdma16);
dsp_reset();
if( SBtype <= 3 )
SbWriteByte(DSPSBProADCMono);
DisableIRQ(SBirq);
#ifdef _MSC_VER
_dos_setvect(SBintnum,old_irq_handler); // Put things back to normal
#else
setvect(SBintnum,old_irq_handler); // Put things back to normal
#endif
}
void interrupt sbpro_bug_handler(void) // To deal with the SBPro's channel swap bug.
{
enable();
inp(DSPIrqAck8Port); // Acknowledge interrupt
dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
SbWriteByte(DSPSetHSDMASize); // Reset size (more than 1 byte :)
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
SbWriteByte(DSPStartHSDMA); // Restart DMA with correct channels
setvect(SBintnum,write_irq_handler);
if( SBirq > 8 )
outp(0xA0,0x20);
outp(0x20,0x20); // Done with interrupt
}
void interrupt null_irq_handler(void)
{
if( dsp_bits == 16 )
inp(DSPIrqAck16Port); // Acknowledge the SB's interupt
else
inp(DSPIrqAck8Port);
if( SBirq > 8 )
outp(0xA0,0x20);
outp(0x20,0x20); // Done with interrupt
}
void interrupt read_irq_handler(void)
{
enable();
if( dsp_bits == 16 )
inp(DSPIrqAck16Port); // Acknowledge the SB's interupt
else
inp(DSPIrqAck8Port);
if( DMABufsFull == BufCount ) // Check for overrun
{
dsp_overrun++; // We've got an overrun. Oops.
BufQueueHead = BufQueueTail = (BufQueueTail+1) % BufCount;
if( dsp_overrun > 16 )
{ // We've got a LOT of overruns. Cancel
dsp_pause_dma(); // DMA. Someone forgot about it.
dma_reset(dsp_bits == 16 ? SBdma16 : SBdma);
DMAStopped=1;
BufQueueHead=0;
BufQueueTail=0;
DMABufsFull =0;
setvect(SBintnum,null_irq_handler);
SbWriteByte(DSPSBProADCMono);
}
}
#ifdef DEBUG_DMASTAT
{
poke(0xB800,154,11824+BufQueueHead);
poke(0xB800,156,11824+BufQueueTail);
poke(0xB800,158,11824+DMABufsFull);
}
#endif
BufQueueTail = (BufQueueTail+1) % BufCount;
DMABufsFull++;
if( SBtype <= 3 )
{
if( dsp_hispeed )
SbWriteByte(DSPStartHSADCDMA);
else
{
SbWriteByte(DSPStartADCDMA);
SbWriteByte((BufLen-1)%256);
SbWriteByte((BufLen-1)/256);
}
}
if( callback_function != NULL )
callback_function();
if( SBirq > 8 )
outp(0xA0,0x20);
outp(0x20,0x20);
}
void interrupt write_irq_handler(void)
{
enable();
if( DMABufsFull <= 1 ) // Are we done playing everything?
{
if( dsp_bits == 16 ) // Yup, ack the soundcard,
inp(DSPIrqAck16Port);
else
inp(DSPIrqAck8Port);
SbWriteByte(DSPPauseDMA); // Kill playback
setvect(SBintnum,null_irq_handler); // Flip to other interrupt handler
dma_reset(dsp_bits == 16 ? SBdma16 : SBdma); // Kill DMA
DMAStopped = 1; // And flag it.
DMABufsFull = 0;
BufQueueHead = 0;
BufQueueTail = 0;
if( SBirq > 8 )
outp(0xA0,0x20);
outp(0x20,0x20); // We're done.
#ifdef DEBUG_DMASTAT
{
poke(0xB800,154,20016+BufQueueHead);
poke(0xB800,156,20016+BufQueueTail);
poke(0xB800,158,20016+DMABufsFull);
}
#endif
return;
}
if( dsp_bits == 16 ) // We're not done. 16 bits?
{
inp(DSPIrqAck16Port); // Yup. Acknowledge it to continue automatically.
BufQueueHead = (BufQueueHead+1) % BufCount; // Mark that buffer as played
DMABufsFull--;
}
else
{
inp(DSPIrqAck8Port); // No, 8 bits. So continue...
BufQueueHead = (BufQueueHead+1) % BufCount; // Mark that buffer as played
DMABufsFull--;
if( GUS )
dma_setup(SBdma,DMABuffers[BufQueueHead],BufLen-1,1);
if( SBtype < 4 ) // Soundblaster 16 continues upon ack.
if( !dsp_hispeed ) // Low-speed DMA mode
{
SbWriteByte(DSPStartDMA);
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
}
else // High-speed DMA mode
SbWriteByte(DSPStartHSDMA);
}
if( callback_function != NULL ) // Tell the caller that we've
callback_function(); // gotten an interrupt.
#ifdef DEBUG_DMASTAT
{
poke(0xB800,154,11824+BufQueueHead);
poke(0xB800,156,11824+BufQueueTail);
poke(0xB800,158,11824+DMABufsFull);
}
#endif
if( SBirq > 8 )
outp(0xA0,0x20);
outp(0x20,0x20); // Complete the interrupt.
}
void dsp_callback(void (*handler)(void))
{
#ifdef DEBUG_PROC
printf("dsp_callback: %p\n",handler);
#endif
callback_function = handler;
}
int dsp_buffers_free(void)
{
int x;
disable();
x = DMABufsFull;
enable();
#ifdef DEBUG_FREE
printf("dsp_buffers_free: %i\n",BufCount-x);
#endif
return BufCount - x;
}
int dsp_active(void)
{
return !DMAStopped;
}
struct dsp_device_caps *dsp_get_device_caps(void)
{
return &SB_caps[SBtype];
}
unsigned dsp_set_record(unsigned speed, int stereo, int bits, int sign)
{
dsp_speaker(0);
if( SBtype >= 3 )
return dsp_set_sample(speed,stereo,bits,sign);
if( SBtype == 2 )
if( speed <= 15151 )
return dsp_set_sample(speed,stereo,bits,sign);
else
{
#ifdef DEBUG_PROC
printf("dsp_set_sample: speed %u %s%i %s\n",speed,stereo ? "stereo " : "", bits, sign ? "signed" : "unsigned");
printf("dsp_set_record: failure\n");
#endif
return 0;
}
if( SBtype == 1 )
if( speed <= 11111 )
return dsp_set_sample(speed,stereo,bits,sign);
else
{
#ifdef DEBUG_PROC
printf("dsp_set_sample: speed %u %s%i %s\n",speed,stereo ? "stereo " : "", bits, sign ? "signed" : "unsigned");
printf("dsp_set_record: failure\n");
#endif
return 0;
}
return 0;
}
unsigned dsp_set_sample(unsigned speed, int stereo, int bits, int sign)
{
int x;
long test;
stereo = !!stereo;
sign = !!sign;
#ifdef DEBUG_PROC
printf("dsp_set_sample: speed %u %s%i %s\n",speed,stereo ? "stereo " : "", bits, sign ? "signed" : "unsigned");
#endif
dsp_pause_dma();
if( !DMAStopped ) // Was a playback going?
if( dsp_bits == 16 ) // Yes... 8 or 16 bit?
dma_reset(SBdma16); // Reset 16 bit DMA
else
dma_reset(SBdma); // Reset 8 bit DMA
disable();
BufQueueHead = BufQueueTail = 0; // Reset DMA variables
enable();
dsp_speed = 0;
if( sign && (SBtype < 4) ) // Check for card's capabilities.
goto fail;
dsp_signed = sign;
if( speed < 4000 )
goto fail;
if( stereo != 0 )
stereo = 1;
if( stereo && (SBtype < 3) )
goto fail;
if( (SBtype < 4) && (bits != 8) )
goto fail;
if( (bits != 8) && (bits != 16 ) )
goto fail;
if( (speed > 22222) & (SBtype == 1) )
goto fail;
if( stereo && (SBtype == 3) )
{
test = speed * 2L;
speed *= 2; // SBPro requires 2x speed for stereo.
}
else
test = speed;
if( test > 45454 )
goto fail;
dsp_set_speed(&speed); // Set speed.
if( SBtype == 3 )
{
x = SbReadMixerReg(0x0E); // Set stereo control bit in mixer
SbWriteMixerReg(0x0E,x & 0xFD);
SbWriteMixerReg(0x0E,(x & 0xFD) + 2*stereo);
}
if( stereo && (SBtype == 3) )
speed /= 2; // Real speed.
dsp_speed = speed; // Set variables for IRQ routines.
dsp_bits = bits;
dsp_stereo = stereo;
disable();
DMAStopped = 1; // Reset DMA variables.
DMABufsFull = 0;
enable();
fail:
#ifdef DEBUG_PROC
printf("dsp_set_playback: %s\n",speed > 0 ? "success" : "failure");
#endif
return dsp_speed; // Return true speed on success.
}
void dsp_set_speed(unsigned *speed)
{
static char time_const;
unsigned speed0,speed1;
if( *speed != 0 )
{
time_const = 0;
if( SBtype >= 4 )
{
if( (*speed >= 44000) && (*speed < 44700) )
{
*speed = 44100;
time_const = 1;
}
if( (*speed >= 22000) && (*speed < 22120) )
{
*speed = 22050;
time_const = 2;
}
if( (*speed >= 11000) && (*speed < 11080) )
{
*speed = 11025;
time_const = 3;
}
}
if( !time_const )
{
if( SBtype >= 2 ) // Can we use high-speed DMA?
{
time_const = (char)((65536L-(256000000L / (long)*speed)) >> 8);
speed0 = (256000000L/(65536L-((long)(time_const )<< 8)));
speed1 = (256000000L/(65536L-((long)(time_const+1)<< 8)));
if( (*speed - speed0) > (speed1 - *speed) )
{
time_const++;
*speed = speed1;
}
else
*speed = speed0;
dsp_speed = *speed;
dsp_hispeed = 1;
}
else
{
time_const = (char)(256-(1000000 / *speed));
speed0 = 1000000 / (256-(time_const ));
speed1 = 1000000 / (256-(time_const+1));
if( (*speed - speed0) > (speed1 - *speed) )
{
time_const++;
*speed = speed1;
}
else
*speed = speed0;
dsp_speed = *speed;
dsp_hispeed = 0;
}
}
}
switch(time_const)
{
case 1:
SbWriteByte(DSPSB16SetSpeed);
SbWriteByte(172); // 44100 / 256
SbWriteByte(68); // 44100 % 256
break;
case 2:
SbWriteByte(DSPSB16SetSpeed);
SbWriteByte(86); // 22050 / 256
SbWriteByte(34); // 22050 % 256
break;
case 3:
SbWriteByte(DSPSB16SetSpeed);
SbWriteByte(43); // 11025 / 256
SbWriteByte(17); // 11025 % 256
break;
default:
SbWriteByte(DSPSetTimeConstant);
SbWriteByteTimeout(time_const,SBTimeOut*5);
break;
}
#ifdef DEBUG_PROC
if( *speed )
printf("dsp_set_speed: true speed %u time constant %i\n",*speed,(int)time_const);
else
printf("dsp_set_speed: resetting time constant %i\n",(int)time_const);
#endif
}
void dsp_pause_dma(void)
{
SbWriteByte(DSPPauseDMA); // Issue Pause DMA command.
#ifdef DEBUG_PROC
printf("dsp_pause_dma\n");
#endif
}
int dsp_continue_dma(void)
{
int x;
disable();
x = DMABufsFull;
enable();
if( x==0 )
return 0; // We're not playing. Forget it.
if( dsp_bits == 16 )
SbWriteByte(DSPContinueDMA); // Kluge for 16-bits. [Shrug]
SbWriteByte(DSPContinueDMA); // Issue Continue DMA command.
#ifdef DEBUG_PROC
printf("dsp_continue_dma\n");
#endif
return 1;
}
void *dsp_open_buf(void)
{
if( DMABufsFull == BufCount ) // Is the buffer full?
{
return NULL;
}
else
{
#ifdef DEBUG_WRITE
printf("dsp_open_buf: success %p\n",DMABuffers[BufQueueTail]);
#endif
bufs_open = 1;
return DMABuffers[BufQueueTail];
}
}
void dsp_close_buf(void)
{
int start;
int tail;
unsigned speed_dummy = 0;
if( !bufs_open )
if( DMABufsFull && DMAStopped )
{
setvect(SBintnum,write_irq_handler); // Set up interrupts.
DMAStopped = 0;
goto start_playback;
}
else
return;
bufs_open = 0;
disable();
#ifdef DEBUG_WRITE
printf("dsp_close_buf\n");
#endif
enable(); // No.
disable();
start = DMAStopped; // Find out if DMA is running currently
enable();
#ifdef AUTO_PREWRITE
if( (start) && (BufQueueTail < (BufCount-1)) ) // If prewrite is
{
disable();
BufQueueHead = 0; // Tell dsp_write we've done it.
BufQueueTail++;
DMABufsFull++;
enable();
#ifdef DEBUG_DMASTAT
poke(0xB800,154,20016+BufQueueHead);
poke(0xB800,156,20016+BufQueueTail);
poke(0xB800,158,20016+DMABufsFull);
#endif
return;
}
#endif
if( !start )
{
disable();
BufQueueTail++; // Move tail to account for it
if( BufQueueTail >= BufCount ) // Wrap tail
BufQueueTail = 0;
DMABufsFull++; // Mark another buffer as full.
enable();
}
else
{
if( SBtype < 4 )
dsp_speaker(1);
disable(); // Set up DMA variables.
BufQueueHead = 0;
BufQueueTail++;
DMABufsFull++;
DMAStopped = 0;
if( BufQueueTail >= BufCount ) // Wrap tail
BufQueueTail = 0;
enable();
start_playback:
if( SBtype >= 4 )
{
setvect(SBintnum,write_irq_handler); // Set up interrupts.
dsp_reset(); // Reset DSP
dsp_set_speed(&speed_dummy); // Set DMA speed.
if( dsp_bits == 8 )
{
dma_reset(SBdma); // Setup 8-bit SB16 DMA
SbWriteByte(DSPSB16Start8DMA);
SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
}
else
{
dma_reset(SBdma16); // Setup 16-bit DMA
SbWriteByte(DSPSB16Start16DMA);
SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
SbWriteByte(((BufLen-2)/2) % 256);
SbWriteByte(((BufLen-2)/2) / 256);
dma_setup(SBdma16,DMABufferBase,BufTotal-1,1);
}
}
else
{
dma_reset(SBdma);
if( dsp_stereo )
{
setvect(SBintnum,sbpro_bug_handler); // (grin)
dma_setup(SBdma,DMABufferBase+1,0,1);
SbWriteByte(DSPStartHSDMA); // To avoid bug in SBPro playback,
SbWriteByte(0); // we have to play back a one-byte sample.
SbWriteByte(0); // How stupid.
}
else
{
setvect(SBintnum,write_irq_handler); // Set up interrupts.
if( GUS )
dma_setup(SBdma,DMABuffers[0],BufLen-1,1);
else
dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
if( dsp_hispeed )
{
SbWriteByte(DSPSetHSDMASize); // High speed mode
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
SbWriteByte(DSPStartHSDMA);
}
else
{
SbWriteByte(DSPStartDMA); // Low speed mode
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
}
}
}
}
#ifdef DEBUG_DMASTAT
poke(0xB800,154,11824+BufQueueHead);
poke(0xB800,156,11824+BufQueueTail);
poke(0xB800,158,11823+DMABufsFull);
#endif
return;
}
int dsp_prewrite(void *buffer)
{
int start;
int tail;
#ifdef DEBUG_WRITE
printf("dsp_prewrite: %p\n",buffer);
#endif
if( BufQueueTail == (BufCount-1) ) // Is there room?
return 0; // No.
disable();
start = DMAStopped; // Are we running already?
enable();
if( !start )
{
return 0; // Yes.
}
else
{
memcpy(DMABuffers[BufQueueTail],buffer,BufLen); // Copy prewrite into buffer
disable();
BufQueueHead = 0; // Tell dsp_write we've done it.
BufQueueTail++;
DMABufsFull++;
enable();
#ifdef DEBUG_DMASTAT
poke(0xB800,154,20016+BufQueueHead);
poke(0xB800,156,20016+BufQueueTail);
poke(0xB800,158,20016+DMABufsFull);
#endif
}
return 1; // Success.
}
int dsp_write(void *buffer)
{
int start;
int tail;
unsigned speed_dummy = 0;
if( buffer == NULL )
if( DMABufsFull && DMAStopped )
{
setvect(SBintnum,write_irq_handler); // Set up interrupts.
DMAStopped = 0;
goto start_playback;
}
else
return 0;
disable();
if( DMABufsFull == BufCount ) // Is the buffer full?
{
enable(); // Yes, can't write.
return 0;
}
#ifdef DEBUG_WRITE
printf("dsp_write: %p\n",buffer);
#endif
enable(); // No.
disable();
start = DMAStopped; // Find out if DMA is running currently
enable();
#ifdef AUTO_PREWRITE
if( (start) && (BufQueueTail < (BufCount-1)) ) // If prewrite is
{
dsp_prewrite(buffer); // requested, do it.
return 1;
}
#endif
if( !start )
{
memcpy(DMABuffers[BufQueueTail],buffer,BufLen); // Copy data into DMA buffer
disable();
BufQueueTail++; // Move tail to account for it
if( BufQueueTail >= BufCount ) // Wrap tail
BufQueueTail = 0;
DMABufsFull++; // Mark another buffer as full.
enable();
}
else
{
if( SBtype < 4 )
dsp_speaker(1);
memcpy(DMABuffers[BufQueueTail],buffer,BufLen); // Copy data into DMA buffer
disable(); // Set up DMA variables.
BufQueueHead = 0;
BufQueueTail++;
DMABufsFull++;
DMAStopped = 0;
if( BufQueueTail >= BufCount ) // Wrap tail
BufQueueTail = 0;
enable();
start_playback: // Go here to start DMA, no questions
if( SBtype >= 4 )
{
setvect(SBintnum,write_irq_handler); // Set up interrupts.
dsp_reset(); // Reset DSP
dsp_set_speed(&speed_dummy); // Set DMA speed.
if( dsp_bits == 8 )
{
dma_reset(SBdma); // Setup 8-bit SB16 DMA
SbWriteByte(DSPSB16Start8DMA);
SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
}
else
{
dma_reset(SBdma16); // Setup 16-bit DMA
SbWriteByte(DSPSB16Start16DMA);
SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
SbWriteByte(((BufLen-2)/2) % 256);
SbWriteByte(((BufLen-2)/2) / 256);
dma_setup(SBdma16,DMABufferBase,BufTotal-1,1);
}
}
else
{
dma_reset(SBdma);
if( dsp_stereo )
{
setvect(SBintnum,sbpro_bug_handler); // (grin)
dma_setup(SBdma,DMABufferBase+1,0,1);
SbWriteByte(DSPSetHSDMASize); // To avoid bug in SBPro playback,
SbWriteByte(0); // we have to play back a one-byte sample.
SbWriteByte(0); // How stupid.
SbWriteByte(DSPStartHSDMA);
}
else
{
setvect(SBintnum,write_irq_handler); // Set up interrupts.
if( GUS )
dma_setup(SBdma,DMABuffers[0],BufLen-1,1);
else
dma_setup(SBdma,DMABufferBase,BufTotal-1,1);
if( dsp_hispeed )
{
SbWriteByte(DSPSetHSDMASize); // High speed mode
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
SbWriteByte(DSPStartHSDMA);
}
else
{
SbWriteByte(DSPStartDMA); // Low speed mode
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
}
}
}
}
#ifdef DEBUG_DMASTAT
poke(0xB800,154,11824+BufQueueHead);
poke(0xB800,156,11824+BufQueueTail);
poke(0xB800,158,11823+DMABufsFull);
#endif
return 1;
}
int dsp_read(void *ptr)
{
unsigned speed_dummy = 0;
#ifdef DEBUG_WRITE
printf("dsp_read: %p\n",ptr);
#endif
if( ptr == NULL )
{
setvect(SBintnum,null_irq_handler);
dma_reset(dsp_bits == 16 ? SBdma16 : SBdma);
if( SBtype == 3 )
SbWriteByte(DSPSBProADCMono);
#ifdef DEBUG_DMASTAT
poke(0xB800,154,7*256+32);
poke(0xB800,156,7*256+32);
poke(0xB800,158,7*256+32);
#endif
return 4;
}
if( DMAStopped )
{
dsp_speaker(0);
if( SBtype == 3 )
if( dsp_stereo )
{
SbWriteByte(DSPSBProADCStereo);
SbWriteMixerReg(0x0E,(SbReadMixerReg(0x0E)&0xFD));
SbWriteMixerReg(0x0C,(SbReadMixerReg(0x0C)|(0x20)));
}
else
{
SbWriteByte(DSPSBProADCMono);
SbWriteMixerReg(0x0E,SbReadMixerReg(0x0E)&0xFD);
if( dsp_speed <= 8000 )
SbWriteMixerReg(0x0C,SbReadMixerReg(0x0C)&(0xDF));
else
SbWriteMixerReg(0x0C,SbReadMixerReg(0x0C)|(0x20));
}
BufQueueHead = 0;
BufQueueTail = 0;
DMABufsFull = 0;
DMAStopped = 0;
dsp_overrun = 0;
setvect(SBintnum,read_irq_handler);
if( SBtype >= 4 )
{
dsp_reset();
dsp_set_speed(&speed_dummy);
if( dsp_bits == 8 )
{
SbWriteByte(DSPSB16Start8ADCDMA);
SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
}
else
{
SbWriteByte(DSPSB16Start16ADCDMA);
SbWriteByte((0x20*dsp_stereo)+(0x10*dsp_signed));
SbWriteByte(((BufLen-2)/2) % 256);
SbWriteByte(((BufLen-2)/2) / 256);
}
dma_setup(dsp_bits == 16 ? SBdma16 : SBdma,DMABufferBase,BufTotal-1,0);
}
else
{
if( dsp_hispeed )
{
SbWriteByte(DSPSetHSDMASize); // High speed mode
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
SbWriteByte(DSPStartHSADCDMA);
}
else
{
SbWriteByte(DSPStartADCDMA); // Low speed mode
SbWriteByte((BufLen-1) % 256);
SbWriteByte((BufLen-1) / 256);
}
dma_setup(SBdma,DMABufferBase,BufTotal-1,0);
}
#ifdef DEBUG_DMASTAT
poke(0xB800,154,11824+BufQueueHead);
poke(0xB800,156,11824+BufQueueTail);
poke(0xB800,158,11824+DMABufsFull);
#endif
return 3;
}
else
if( BufQueueHead == BufQueueTail )
return 2;
else
{
memcpy(ptr,DMABuffers[BufQueueHead],BufLen);
disable();
BufQueueHead = (BufQueueHead+1)%BufCount;
enable();
DMABufsFull--;
#ifdef DEBUG_DMASTAT
poke(0xB800,154,11824+BufQueueHead);
poke(0xB800,156,11824+BufQueueTail);
poke(0xB800,158,11824+DMABufsFull);
#endif
if( dsp_overrun > 0 )
{
dsp_overrun = 0;
return 1;
}
else
return 0;
}
}
int sb_get_params(int *port, int *dma, int *irq, int *dma16)
{
char *t, *t1, *blaster;
/* Set arguments to reasonable values (Soundblaster defaults) */
*port = 0x220;
*irq = 5;
*dma = 1;
*dma16 = 5;
/* Attempt to read environment variable */
t = getenv("BLASTER");
/* Is the environment variable set? */
if(t == NULL)
return 0;
/* Duplicate the string so that we don't trash our environment */
blaster = strdup(t);
/* Now parse the BLASTER variable */
t = strtok(blaster," \t");
while(t)
{
switch(toupper(t[0]))
{
case 'A': /* I/O address */
*port = (int)strtol(t+1,&t1,16);
break;
case 'I': /* Hardware IRQ */
*irq = atoi(t+1);
break;
case 'D': /* DMA channel */
*dma = atoi(t+1);
break;
case 'H':
*dma16 = atoi(t+1);
break;
}
t = strtok(NULL," \t");
}
#ifdef DEBUG_PROC
printf("sb_get_params: A%3X I%i D%i H%i\n",*port,*irq,*dma,*dma16);
#endif
free(blaster);
return 1;
}
int mix_reset(void)
{
SbWriteMixerReg(0,0);
return !!(SbReadMixerReg(4));
}
unsigned char mix_read(int device, int channel)
{
int r1;
int ll=0,lr=0;
if( device > 8 )
channel = SBtype >= 4 ? MIXleft : MIXright;
if( SBtype <= 3 && SBProMixRegs[device] )
{
r1 = SbReadMixerReg(SBProMixRegs[device]); // Read register
ll = (r1 & 0xF0); // Left channel: High nibble
lr = (r1 & 0x0F) << 4; // Right channel: Low nibble
}
if( SBtype >= 4 && SB16MixRegs[device] )
{
ll = SbReadMixerReg(SB16MixRegs[device]); // Given byte: Left
lr = SbReadMixerReg(SB16MixRegs[device]+1); // Byte+1: Right
}
if( (SBtype <= 3) && (device == MIXmicrophone) )
lr <<= 1;
#ifdef DEBUG_PROC
printf("mix_read: %i %i (%i %i)\n",device,channel,ll,lr);
#endif
switch( channel )
{
case MIXleft: return ll;
case MIXright: return lr;
case MIXboth: return (ll+lr)/2;
}
return 0;
}
void mix_write(int device, int channel, unsigned char level)
{
int r1,r2;
#ifdef DEBUG_PROC
printf("mix_write: device %i channel %i level %i\n",device,channel,(int)level);
#endif
if( device > 8 )
channel = SBtype >= 4 ? MIXleft : MIXright;
if( (SBtype <= 3) && (device == MIXmicrophone) )
level >>= 1;
if( SBtype <= 3 && SBProMixRegs[device] ) // Soundblaster Pro shares each register between two channels
{
r1 = SbReadMixerReg(SBProMixRegs[device]);
switch( channel ) // We have to set the correct part of the byte.
{
case MIXright: r2 = (r1 & 0xF0) + (level>>4); break;
case MIXleft: r2 = (r1 & 0x0F) + (level & 0xF0); break;
case MIXboth: r2 = (level & 0xF0) + (level>>4); break;
}
SbWriteMixerReg(SBProMixRegs[device],r2);
}
if( SBtype >= 4 && SB16MixRegs[device] ) // Soundblaster 16 uses register per channel
switch( channel ) // Makes things a bit easier, no?
{
case MIXleft: SbWriteMixerReg(SB16MixRegs[device] ,level); break;
case MIXright: SbWriteMixerReg(SB16MixRegs[device]+1,level); break;
case MIXboth: SbWriteMixerReg(SB16MixRegs[device] ,level);
SbWriteMixerReg(SB16MixRegs[device]+1,level); break;
}
}
void mix_set_sb16_output(int value)
{
#ifdef DEBUG_PROC
printf("mix_set_sb16_output: %i\n",value);
#endif
SbWriteMixerReg(0x3C,value); // Send output value raw :)
}
void mix_set_sb16_input(int channel, int value)
{
#ifdef DEBUG_PROC
printf("mix_set_sb16_value: channel %i value %i\n",channel,value);
#endif
switch( channel )
{
case MIXboth: SbWriteMixerReg(0x3E,value);
case MIXleft: SbWriteMixerReg(0x3D,value); break;
case MIXright: SbWriteMixerReg(0x3E,value); break;
}
}
void mix_set_input(int value)
{
int i;
#ifdef DEBUG_PROC
printf("mix_set_input: %x",value);
#endif
SbReadMixerReg(0x0C); // Read input control byte
/* bit: 7 6 5 4 3 2 1 0 F=frequency (0=low, 1=high)
x x T x F S S x SS=source (00=MIC, 01=CD, 11=LINE)
T=input filter switch (ANFI) */
// i &= 0xF9;
i |= 0x20;
switch( value )
{
case 0x18: i |= 6; break;
case 0x06: i |= 2; break;
case 0x01: break;
}
SbWriteMixerReg(0x0C,i); // Write input control byte
}